package cn.duhengkui.national.valuelist.core.data.aop;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.IntroductionInterceptor;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.CollectionUtils;

import cn.duhengkui.national.valuelist.annotation.Command;
import cn.duhengkui.national.valuelist.annotation.Query;
import cn.duhengkui.national.valuelist.core.data.CommandExecutor;
import cn.duhengkui.national.valuelist.core.data.exception.DAORunTimeException;
import cn.duhengkui.national.valuelist.core.data.impl.BaseDaoImpl;
import cn.duhengkui.national.valuelist.core.data.util.DaoEntityThreadLocal;
import cn.duhengkui.national.valuelist.core.data.util.GenericsUtils;
import cn.duhengkui.national.valuelist.core.data.util.SimpleStatementBuilder;
import cn.duhengkui.national.valuelist.core.data.util.TokenReplaceTextManipulator;
import cn.duhengkui.national.valuelist.core.entity.BaseEntity;
import cn.duhengkui.national.valuelist.core.entity.BaseQueryObject;
import cn.duhengkui.national.valuelist.core.entity.PaginatedResult;
import cn.duhengkui.national.valuelist.core.entity.SqlAndParams;



/**   
 * <table border="1">
 *	<tr><td>包名称：</td><td>cn.duhengkui.national.valuelist.core.data.aop</td></tr>
 *	<tr><td>类名称：</td><td>FinderIntroductionInterceptor</td></tr>
 *	<tr><td>类描述：</td><td>DAO拦截规则（DAO拦截器）</td></tr>
 *	<tr><td>创建人：</td><td>杜恒奎</td></tr>
 *  <tr><td>创建时间：</td><td>2018年6月7日/下午3:14:08</td></tr> 
 *  <tr><td>版本：</td><td>1.0.0</td></tr>
 * </table>   
 *     
 */
public class FinderIntroductionInterceptor<Entity extends BaseEntity> implements IntroductionInterceptor {
	/**
	 * Logger for this class
	 */
	protected static final Logger logger = LoggerFactory.getLogger(FinderIntroductionInterceptor.class);
	


	/* (non-Javadoc)
	 * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
	 */
	@SuppressWarnings("unchecked")
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    	Method method = methodInvocation.getMethod();
    	int methodType = MethodUtil.getExecuteType(method);
    	if(methodType == MethodUtil.DEFAULT) {
			Class<Entity> type = (Class<Entity>)getClass(method, methodInvocation);
			DaoEntityThreadLocal.set(type);
			return methodInvocation.proceed();
		} else if(methodType == MethodUtil.COMMAND) {
			CommandExecutor commandExecutor = (CommandExecutor) methodInvocation.getThis();
			Command command = method.getAnnotation(Command.class);
			String statement = command.value();
			Object[] arguments = methodInvocation.getArguments();
			Map<String, Object> parameterMap = MethodUtil.getParameterMap(arguments, method);
			TokenReplaceTextManipulator handler = new TokenReplaceTextManipulator();
			String realStatement = handler.manipulate(new StringBuilder(statement), parameterMap).toString();
			commandExecutor.execute(realStatement);
			return null;
		} else {
			
			Class<Entity> type = (Class<Entity>)getClass(method, methodInvocation);
			BaseDaoImpl<Entity> extendDao = (BaseDaoImpl<Entity>)methodInvocation.getThis();
    		DaoEntityThreadLocal.set(type);
    		Object[] arguments = methodInvocation.getArguments();
    		Query batch = method.getAnnotation(Query.class);
    		String sql = batch.sql();
    		Class<? extends BaseQueryObject> qclazz = batch.returnQueryObj();
    		Map<String,Object> agumentMap = MethodUtil.getParameterMap(arguments, method);
    		List<Map<String,Object>> agumentList = MethodUtil.getParameterList(arguments, method);
    		
    		logger.debug("national-data-->cn.duhengkui.national.valuelist.core.data.aop.FinderIntroductionInterceptor:" + sql);
    		
    		if(methodType == MethodUtil.LIST) {
    			SqlAndParams sqlAndParams = SimpleStatementBuilder.generate(new StringBuilder(sql), agumentMap);
    			if(qclazz.equals(BaseQueryObject.class)){
    			    return extendDao.query(sqlAndParams.getSql(), sqlAndParams.getParams().toArray());
    			}else{
    				return extendDao.query(sqlAndParams.getSql(), sqlAndParams.getParams().toArray(),qclazz);
    			}
    		}else if(methodType == MethodUtil.BATCH) {
    			String batchSql = null;
    			List<Object[]> paramsList = new ArrayList<>();
    			for(int i = 0; i < agumentList.size();i++){
    				agumentList.get(i).putAll(agumentMap);
    				SqlAndParams sqlAndParams = SimpleStatementBuilder.generate(new StringBuilder(sql), agumentList.get(i));
    				paramsList.add(sqlAndParams.getParams().toArray());
    				if(i == 0){
    					batchSql = sqlAndParams.getSql();
    				}
    			}
    			return extendDao.batchUpdate(batchSql, paramsList);
    		} else if(methodType == MethodUtil.PAGE) {
    			SqlAndParams sqlAndParams = SimpleStatementBuilder.generate(new StringBuilder(sql), agumentMap);
    			Object pr = null;
    			List<Object> list = CollectionUtils.arrayToList(arguments);
   			 	for(Object obj : list) {
   				 if(obj instanceof PaginatedResult<?>) {
   					 pr = obj;
   					 break;
   				 }
   			 	}
   			 	if(qclazz.equals(BaseQueryObject.class)){
   			 		return extendDao.query(sqlAndParams.getSql(), (PaginatedResult<Entity>)pr, sqlAndParams.getParams().toArray());
   			 	}else{
   			 		return extendDao.query(sqlAndParams.getSql(), (PaginatedResult<BaseQueryObject>)pr, sqlAndParams.getParams().toArray(),qclazz);
   			 	}
    		} else if(methodType == MethodUtil.BATCH_DELETE) {
    			SqlAndParams sqlAndParams = SimpleStatementBuilder.generate(new StringBuilder(sql), agumentMap);
    			return extendDao.batchRemove(sqlAndParams.getSql(), sqlAndParams.getParams().toArray());
    		} else if(methodType == MethodUtil.SINGLE) {
    			SqlAndParams sqlAndParams = SimpleStatementBuilder.generate(new StringBuilder(sql), agumentMap);
    			if(qclazz.equals(BaseQueryObject.class)){
    				return extendDao.single(sqlAndParams.getSql(), sqlAndParams.getParams().toArray());
    			}else{
    				return extendDao.single(sqlAndParams.getSql(), sqlAndParams.getParams().toArray(),qclazz);
    			}
    		}else if(methodType == MethodUtil.FREE_LIST) {
    			SqlAndParams sqlAndParams = SimpleStatementBuilder.generate(new StringBuilder(sql), agumentMap);
    			return extendDao.queryFree(sqlAndParams.getSql(), sqlAndParams.getParams().toArray());
    		}else if(methodType == MethodUtil.UPDATE) {
    			SqlAndParams sqlAndParams = SimpleStatementBuilder.generate(new StringBuilder(sql), agumentMap);
    			return extendDao.update(sqlAndParams.getSql(), sqlAndParams.getParams().toArray());
    		} else {
    			if(TransactionSynchronizationManager.isCurrentTransactionReadOnly()){
    				throw new DAORunTimeException("只读数据源不可更新");
    			}
    			return methodInvocation.proceed();
    		}
    	}
    }

    /* (non-Javadoc)
     * @see org.springframework.aop.DynamicIntroductionAdvice#implementsInterface(java.lang.Class)
     */
    public boolean implementsInterface(@SuppressWarnings("rawtypes") Class intf) {
        return intf.isInterface() && BaseDaoImpl.class.isAssignableFrom(intf);
    }
        
    private Class<?> getClass(Method method, MethodInvocation methodInvocation) {
    	if("update".equals(method.getName())
        		|| "delete".equals(method.getName())
        		|| "save".equals(method.getName())
        		|| "saveOrUpdate".equals(method.getName())
        		|| "load".equals(method.getName())) {
//        		return method.getParameterTypes()[0];
    		return methodInvocation.getArguments()[0].getClass();
    	} else if("getDataBaseTime".equals(method.getName()) || "getDataBaseTimestamp".equals(method.getName())) {
    		return BaseEntity.class;
    	} else {
    		return GenericsUtils.getClassGenricType(method.getDeclaringClass());
    	}
    }
    
}